﻿#region Using directives

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.Threading;

#endregion
using System.Diagnostics;
using HTTPMsgAPI;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Lifetime;
using System.IO;
using System.Collections.Specialized;
using timers = System.Timers;
using Thinxoft.HttpLib;
using System.Runtime.Serialization.Formatters.Binary;




namespace HTTPListProc
{
   
    delegate HttpListener.RequestData StartHandleRequestDelegate(TcpClient soc);

    static class HttpListener
    {    

        public struct RequestData
        {
            public byte[] Request;
            public StreamReader Reader;
        }

        static Queue<TcpClient> clientConnections = new Queue<TcpClient>();
        static ServerState state;
        static Settings settings;

        private static ServerState LoadSettings()
        {
            settings  = new Settings();
            state = new ServerState();
            state.LoadSettings(ref settings);
            return state;
        }
       

        [STAThread]
        public static void Main(string[] args)
        {
            try
            {
                LoadSettings();

                Application.ApplicationExit += new EventHandler(Application_ApplicationExit);


                Mutex mx = new Mutex(true, "listener");

                if (mx.WaitOne(1, false))
                {
                    TcpListener list = new TcpListener(IPAddress.Any, 4274);
                    list.Start();           
                    LoadHandlers();

                    timers.Timer timer = new System.Timers.Timer();
                    timer.Enabled = true;
                    timer.Interval = 1;
                    #region [Timer]
                    timer.Elapsed += delegate(object sender, System.Timers.ElapsedEventArgs e)
                    {
                        if (clientConnections.Count > 0)
                        {
                            try
                            {

                                TcpClient soc = clientConnections.Dequeue();
                                if (soc.Connected)
                                {
                                    NetworkStream ns = soc.GetStream();
                                    try
                                    {

                                        RequestData rd = HandleRequest(soc);

                                        Dictionary<string, string> headers = BuildRequestHeaders(rd.Request);
                                        Dictionary<string, string> postParams = GetRequestParameters(headers, rd.Reader);


                                        //gets next request handler from pool
                                        HandlerProxy proxy = HandlerPool.GetNextRequestHandler();
                                        byte[] b = proxy.HandlerInstance.HandleRequest(rd.Request, headers, postParams);

                                        ns.Write(b, 0, b.Length);
                                        ns.Flush();
                                        ns.Close();

                                    }
                                    catch (BadURLException)
                                    {
                                        byte[] b = WriteResponse(string.Format("The specified URL is not valid"), 200);
                                        ns.Write(b, 0, b.Length);
                                        ns.Flush();
                                        ns.Close();

                                    }
                                    catch (Exception ex)
                                    {
                                        byte[] b = WriteResponse(string.Format("An error [{0}] occured of type [{1}]", ex.Message, ex.StackTrace), 200);
                                        ns.Write(b, 0, b.Length);
                                        ns.Flush();
                                        ns.Close();
                                    }
                                }


                            }
                            catch (Exception ex)
                            {
                                ServerState.Log("{0}:{1}", ex.Message, ex.StackTrace);
                            }
                        }
                    };
                    #endregion
                    timer.Start();
                    int count = 0;

                    while (true)
                    {
                        try
                        {
                            TcpClient soc = list.AcceptTcpClient();
                            clientConnections.Enqueue(soc);
                            
                            
                            count++;                            
                        }
                        catch (Exception ex)
                        {
                            ServerState.Log("{0}:{1}", ex.Message, ex.StackTrace);
                        }
                    }
                    HandlerPool.Dispose(); //distroy all proxies in pool
                }
                else
                {
                    ServerState.Log("Http Listener is already running");
                }
            }
            catch (Exception ex)
            {
                ServerState.Log("{0}:{1}", ex.Message, ex.StackTrace);
              
            }
        }

        static void Application_ApplicationExit(object sender, EventArgs e)
        {
            HandlerPool.Dispose();
            foreach (Process p in Process.GetProcessesByName("xedus.HttpReqMan"))
            {
                p.Kill();
            }
        }

        private static void LoadHandlers()
        {
            int handlerInstances = state.XedusSettings.RequestHandlerInstances;
            if (handlerInstances <= 0)
                handlerInstances = 1;


            int handlercount = 0;

          //  MessageBox.Show(Process.GetProcessesByName("Xedus.HttpReqMan.vshost").Length.ToString());
           // Application.Exit();
            //reclaim old processes
            foreach (Process proc in Process.GetProcessesByName("Xedus.HttpReqMan"))
            {
                if (handlercount >= handlerInstances)
                    return;

                HandlerProxy proxy = new HandlerProxy(handlercount,proc);
                HandlerPool.AddRequestHandlerProxy(proxy);
                handlercount++;
            }

            
            
            
            for (int i = handlercount; i < handlerInstances; i++)
            {
                HandlerProxy proxy = new HandlerProxy(i);
                HandlerPool.AddRequestHandlerProxy(proxy);               
            }
        }

       

        #region [Request Handling]
        static RequestData HandleRequest(TcpClient soc)
        {
            NetworkStream ns = soc.GetStream();
            return ParseRequest(ns);
        }

        static RequestData ParseRequest(NetworkStream ns)
        {

            System.Text.StringBuilder sb = new System.Text.StringBuilder();

            StreamReader sr = new StreamReader(ns);
            //iterate through each line and place in a stringbuilder
            while (true)
            {
                string tmp = sr.ReadLine();
                if (tmp == "" || tmp == null)
                    break;
                sb.Append(tmp.ToLower().Trim() + "\n");
            }

            RequestData data;
            data.Request = System.Text.Encoding.ASCII.GetBytes(sb.ToString());
            data.Reader = sr;
            return data;

        }
        #endregion
        

        static Dictionary<string,string> BuildRequestHeaders(byte[] data)
        {
            try
            {
                Dictionary<string, string> httpHeaders = new Dictionary<string, string>();

                //first build http headers
                byte[] buffer = data;
                string request = System.Text.Encoding.ASCII.GetString(buffer);
                string[] headers = request.Split("\n".ToCharArray());
                bool firstLine = true;  //signifies to engine that the first http request line is always processed first
                foreach (string header in headers)
                {
                    //since the first line is processed differently
                    if (firstLine)
                    {
                        string[] requestHeader = header.Split(" ".ToCharArray());
                        if (requestHeader.Length > 1)
                        {
                            if (requestHeader[1].Contains(".."))
                                throw new BadURLException();
                            Uri uri = new Uri("http://fakedomain" + requestHeader[1]);


                            
                            httpHeaders.Add("method", requestHeader[0]);
                            httpHeaders.Add("resource", uri.AbsolutePath);
                            httpHeaders.Add("query", uri.Query);
                            httpHeaders.Add("HttpVersion", requestHeader[2]);
                            firstLine = false;
                        }
                    }
                    else
                    {
                        string[] nameVal = header.Split(":".ToCharArray());
                        if (nameVal.Length > 1)
                        {
                            httpHeaders.Add(nameVal[0].Trim(), nameVal[1].Trim());
                        }
                    }
                }
                return httpHeaders;
            }
            catch (Exception ex)
            {
                ServerState.Log("{0}:{1}", ex.Message, ex.StackTrace);
            }

            return null;
        }

        static Dictionary<string,string> GetRequestParameters(Dictionary<string,string> httpHeaders, StreamReader sr)
        {
            Dictionary<string,string> postParams = new Dictionary<string,string>();

            object length = null;
            if (httpHeaders.ContainsKey ("content-length"))
                length = httpHeaders["content-length"];



            if (length != null)
            {
                int contentLength = int.Parse((string)length);
                char[] postData = new char[contentLength];
                sr.Read(postData, 0, postData.Length);
                string post = new string(postData);

                string[] parameters = post.Split("&".ToCharArray());
                foreach (string parameter in parameters)
                {
                    string[] nameVal = parameter.Split("=".ToCharArray());
                    string name = nameVal[0];
                    postParams.Add(name, nameVal[1]);
                }
            }

            return postParams;

        }

        static byte[] WriteResponse(string message, int code)
        {
            String headerStr = "HTTP/1.1 " + code + " \r\n" +
                "Allow: GET, POST, HEAD\n" +
                "MIME-Version: 1.0\n" +
                "Server: Xedus\n" +
                "Content-Type: text/plain \n" +
                "Content-Length: " + message.Length +
                "\n\n" + message;

            //get bytes from string
            byte[] response = System.Text.Encoding.ASCII.GetBytes(headerStr);
            return response;

        }
    }


}